#include "../config.h"

#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus-shared.h>

#include "application.h"
#include "command.h"
#include "log.h"
#include "dbusservice.h"

#include "dbus-defs.h"

namespace {
gpyp::RefChannel get_ch(GpypDBusService *ds,
                        const gchar    *str) {
  using namespace gpyp;
  static RefRegex re = Regex::create(format("[0-9a-fA-F]{%i}",
                                            ID::LENGTH_STRING).c_str(),
                                     true);
  Glib::ustring id_s;
  if (re->search(str, id_s)) {
    ID id(id_s);
    RefChannel ch = ds->app_->ypcollection()->find(id);
    if (!ch) ch = ds->app_->ypcollection()->find_ended(id);
    return ch;
  }
  return RefChannel();
}
}

#define PLAY(NAME)                                                      \
  gboolean gpyp_dbus_##NAME(GpypDBusService *ds,                        \
                            const gchar    *str,                        \
                            gboolean       *res) {                      \
    using namespace gpyp;                                               \
    RefChannel ch = get_ch(ds, str);                                    \
    if (ch) {                                                           \
      gpyp::log::log << "DBus-service "#NAME": "                        \
                     << ch->get_name() << gpyp::log::endl;              \
      *res = ds->app_->commandrunner().NAME(ch) ? TRUE : FALSE;         \
    } else {                                                            \
      gpyp::log::err << "DBus-service "#NAME": failed to find channel - " \
                     << str << gpyp::log::endl;                         \
      *res = FALSE;                                                     \
    }                                                                   \
    return TRUE;                                                        \
  }
PLAY(play);
PLAY(record);
#undef PLAY

gboolean gpyp_dbus_get_auto_play(GpypDBusService *ds,
                                 gboolean       *res) {
  *res = *ds->app_->auto_play() ? TRUE : FALSE;
  return TRUE;
}
gboolean gpyp_dbus_set_auto_play(GpypDBusService *ds,
                                 gboolean        auto_play) {
  ds->app_->auto_play() = auto_play ? true : false;
  return TRUE;
}

gboolean gpyp_dbus_get_mute(GpypDBusService *ds,
                            gboolean       *res) {
  *res = *ds->app_->conf().sound()->mute() ? TRUE : FALSE;
  return TRUE;
}
gboolean gpyp_dbus_set_mute(GpypDBusService *ds,
                            gboolean        mute) {
  ds->app_->conf().sound()->mute() = mute ? true : false;
  return TRUE;
}

#include "dbus-object.h"

//========================
// GpypDBusService
static void gpyp_dbus_service_init(GpypDBusService */*ds*/) {
}
static void gpyp_dbus_service_class_init(GpypDBusServiceClass *klass) {
  dbus_g_object_type_install_info(G_TYPE_FROM_CLASS(klass),
                                  &dbus_glib_gpyp_dbus_object_info);
}
guint gpyp_dbus_service_get_type() {
  static guint ds_type = 0;
  if (!ds_type) {
    static const GTypeInfo ds_info = {
      sizeof(GpypDBusServiceClass),
      NULL, /* base_init */
      NULL, /* base_finalize */
      (GClassInitFunc)gpyp_dbus_service_class_init,
      NULL, /* class_finalize */
      NULL, /* class_data */
      sizeof(GpypDBusService),
      0,    /* n_preallocs */
      (GInstanceInitFunc)gpyp_dbus_service_init,
      NULL  /* value_table */
    };
    ds_type = g_type_register_static(G_TYPE_OBJECT,
                                     "GpypDBusService",
                                     &ds_info,
                                     (GTypeFlags)0);
  }
  return ds_type;
}
GpypDBusService *gpyp_dbus_service_new(gpyp::Application &app) {
  GpypDBusService *obj =
    GPYP_DBUS_SERVICE(g_object_new(GPYP_TYPE_DBUS_SERVICE, NULL));
  obj->app_ = &app;
  return obj;
}

//========================
DBusGConnection *dbus_create_service_instance(const char *service_name) {
  GError *err = NULL;
  DBusGConnection *conn = dbus_g_bus_get(DBUS_BUS_SESSION, &err);
  if (!conn) {
    gpyp::log::err << "DBus-service: " << err->message << gpyp::log::endl;
    g_error_free(err);
    return NULL;
  }
  if (err) g_error_free(err);

  guint req_name_res = 0;
  gboolean res = FALSE;
  {
    DBusGProxy *proxy = dbus_g_proxy_new_for_name(conn,
                                                  DBUS_SERVICE_DBUS,
                                                  DBUS_PATH_DBUS,
                                                  DBUS_INTERFACE_DBUS);
    if (proxy) {
      res = dbus_g_proxy_call(proxy,
                              "RequestName",
                              NULL /*&err*/,
                              G_TYPE_STRING, service_name,
                              G_TYPE_UINT, 0,
                              G_TYPE_INVALID,
                              G_TYPE_UINT, &req_name_res,
                              G_TYPE_INVALID);
      g_object_unref(G_OBJECT(proxy));
    }
  }

  if (!res) {
    gpyp::log::err << "DBus-service: failed to request service name"
                   << gpyp::log::endl;
    dbus_g_connection_unref(conn);
    return NULL;
  }

  if (req_name_res != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
    gpyp::log::err << "DBus-service: service already registered"
                   << gpyp::log::endl;
    dbus_g_connection_unref(conn);
    return NULL;
  }
  return conn;
}

namespace gpyp {
namespace dbus {

namespace {
DBusGConnection *_connection = NULL;
GpypDBusService  *_service     = NULL;
}

void init(Application &app) {
  if ((_connection = dbus_create_service_instance(DBUS_NAME))) {
    _service = gpyp_dbus_service_new(app);
    dbus_g_connection_register_g_object(_connection,
                                        DBUS_PATH,
                                        G_OBJECT(_service));
  }
}

void uninit() {
  if (_connection) {
    dbus_g_connection_unref(_connection);
    _connection = NULL;
  }
  if (_service) {
    g_object_unref(_service);
    _service = NULL;
  }
}

}/*dbus*/
}/*gpyp*/
